package com.jiadao.service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.google.gson.JsonObject;
import com.jiadao.model.AjaxResult;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.lang3.StringUtils;
import org.nutz.dao.entity.Record;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class ProcessService{


    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;


    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ManagementService managementService;


    @Autowired
    DB db;

    @Autowired
    AssigneeService assigneeService;
    

    /**
     * 获取可执行的流程列表
     */
    public List<ProcessDefinition> getProcessList(){
        List<ProcessDefinition> list = repositoryService
                        .createProcessDefinitionQuery()
                        .orderByProcessDefinitionVersion().asc()//使用流程定义的版本升序排列
                        .list();
        /*
         * Map<String,ProcessDefinition>
         * map集合的key:流程定义的key
         * map集合的value:流程定义的对象
         * 特点：当map集合key值相同的情况下，后一次的值将替换前一次的值
         */
        Map<String,ProcessDefinition> map = new LinkedHashMap<String,ProcessDefinition>();
        if(list != null && list.size() >0){
            for(ProcessDefinition pd:list){
                if(StringUtils.isBlank(pd.getName()) ||StringUtils.isBlank(pd.getDeploymentId()) ){
                    continue;
                }
                map.put(pd.getKey(), pd);
            }
        }
        List<ProcessDefinition> pdList = new ArrayList<ProcessDefinition>(map.values());
        if(pdList!=null && pdList.size()>0){
            for(ProcessDefinition pd:pdList){
                System.out.println("流程定义ID:"+pd.getId());//流程定义的key+版本+随机生成数
                System.out.println("流程定义的名称:"+pd.getName());//对应helloworld.bpmn文件中的name属性值
                System.out.println("流程定义的key:"+pd.getKey());//对应helloworld.bpmn文件中的id属性值
                System.out.println("流程定义的版本:"+pd.getVersion());//当流程定义的key值相同的相同下，版本升级，默认1
                System.out.println("资源名称bpmn文件:"+pd.getResourceName());
                System.out.println("资源名称png文件:"+pd.getDiagramResourceName());
                System.out.println("部署对象ID："+pd.getDeploymentId());
                System.out.println("#########################################################");
            }
        }    
        List<ProcessDefinition> processDefinitions=new LinkedList<ProcessDefinition>(map.values());
        return processDefinitions;
    }

    /**
     * 启动流程实例，默认完成第一个节点的任务，要求第一个节点是提交申请节点。
     * @param processKey  业务流程定义标示 key
     * @param businessKey 业务对象id
     * @param applyUserId 申请人id
     * @param formMap 申请人表单的map，必须包含userId
     * @return   流程实例id
     * 
     */
    public String startProcess(String processKey, String businessKey,String applyUserId,String applyUserName,String title ,Map<String, Object> formMap){
       
        identityService.setAuthenticatedUserId(applyUserId);//申请人ID
        ProcessInstance processInstance = runtimeService // 启动流程时设置业务 key
                .startProcessInstanceByKey(processKey, businessKey,formMap);
        String processInstanceId = processInstance.getId();
        // String title = (String)formMap.get("instance_title");
        runtimeService.setProcessInstanceName(processInstanceId,title);
        log.info("任务processInstanceId:{} 已提交 applyUserId:{},applyUserId:{},businessKey:{},title:{}",processInstanceId,applyUserId,applyUserName,businessKey,title);
        //提交时，设置下一节点审批人
        setNextAssignee(processInstanceId);
        //提交表单之后，自动完成第一个节点
        formMap.clear();
        completeTask(processInstanceId,applyUserId,applyUserName, "0","提交申请",formMap);
        return processInstanceId;
    }

    //完成当前节点操作，流转到下一个处理人
    public void completeTask(String processInstanceId,
                            String userId,
                             String userName,String result,
                             String msg , Map<String, Object> variables){
        ProcessInstance  instance = runtimeService.createProcessInstanceQuery()//获取查询对象
                                    .processInstanceId(processInstanceId)//根据id查询流程实例
                                    .singleResult();
        if(instance == null){
            log.info("任务已经完成 processInstanceId "+processInstanceId);
            return ;
        }
        Task task = getNextTask(processInstanceId);
        log.info("======1 completeTask ======next_task_id:{}",task.getId());
        Authentication.setAuthenticatedUserId(userId);//批注人的名称  一定要写，不然查看的时候不知道人物信息
        
        JsonObject msgObj = new JsonObject();
        msgObj.addProperty("userName", userName);//审批人名称
        msgObj.addProperty("result", result);//审批结果：同意还是拒绝
        msgObj.addProperty("msg", msg);//审批语
        
        taskService.addComment(task.getId(),processInstanceId,msgObj.toString());
        if("-1".equals(result)){
            //驳回  
            //1. 将当前的任务节点的下一个任务节点指定为指定的驳回任务节点
            //2. 将指定任务（目标任务）节点之后的流程轨迹，清空。
            managementService.executeCommand(new RollbackFirstTaskCmd(task.getId()));
            log.info("任务processInstanceId:{} 已被驳回到发起人 {}/{}处理 结果: {}, {}",processInstanceId,userName,userId,result,msg);
        
        }else{
            //“0”：初次提交，1：同意，2:重新提交，3：加签
            taskService.complete(task.getId(),variables);
            log.info("任务processInstanceId:{} taskId:{} 已被{}/{}处理 结果: {}, {}",processInstanceId,task.getId(),userName,userId,result,msg);
        
            setNextAssignee(processInstanceId);
        }
    }

    private void setNextAssignee(String processInstanceId) {
        //设置下一节点的审批人
        Task nextTask = getNextTask(processInstanceId);
        if(nextTask != null){
            log.info("======2 setNextAssignee======task_id:{} ,task_name: {},task_cate:{}",nextTask.getId(),nextTask.getName(),nextTask.getCategory());
            
            String assigneeCode = nextTask.getAssignee();
            if(StringUtils.isBlank(assigneeCode)){
                log.info("获取下一审批人代码失败，请检查流程节点的审批人："+nextTask.getName());
                return;
                //throw new RuntimeException("获取下一审批人代码失败，请检查流程节点的审批人："+nextTask.getName());
            }
            // nextTask.setAssignee(assignee);
            String nextUserId = assigneeService.getNextAssigneeId(assigneeCode);
            if(StringUtils.isBlank(nextUserId)){
                log.info("获取下一审批人失败,检查审批人代码:"+assigneeCode);
                return;
                //throw new RuntimeException("获取下一审批人失败,审批人代码:"+assigneeCode);
            }
            taskService.setAssignee(nextTask.getId(), nextUserId);
            log.info("任务processInstanceId:{} 分配下一审批人{} ",processInstanceId,nextUserId);
        }
    }

    private Task getNextTask(String processInstanceId) {
        Task task = taskService.createTaskQuery()//创建查询对象
                    .processInstanceId(processInstanceId)//通过流程实例id来查询当前任务
                    .singleResult();
        return task;
    }

    public List<Comment> getCommentList(String processInstanceId){
        return taskService.getProcessInstanceComments(processInstanceId);
    }


    /**
     * 根据业务查询用户待办任务列表。
     *
     * @param processKey 流程定义key
     * @param assignee 用户
     * @return 任务列表
     */
    public AjaxResult queryTaskList(String assignee,int pageIndex,int pageSize) {

        /*select 
        t.ID_ as task_id , t.PROC_INST_ID_ as instance_id , t.NAME_ as task_name, e.BUSINESS_KEY_ as data_id ,p.NAME_ as process_name 
        from ACT_RU_TASK t ,ACT_RU_EXECUTION e ,ACT_RE_PROCDEF p
        where t.PROC_INST_ID_  = e.PROC_INST_ID_ 
        and e.PROC_DEF_ID_ = p.ID_
        and p.KEY_ = 'leave' and t.ASSIGNEE_ = 'lisi' and  e.BUSINESS_KEY_ > 0
        */
        String sql = "select t.CREATE_TIME_  as create_time, t.ID_ as task_id , t.PROC_INST_ID_ as instance_id ," +
        " e.start_user_id_ as user_id, e.NAME_ as instance_title, t.NAME_ as task_name, e.BUSINESS_KEY_ as data_id ,"+
        " p.NAME_ as process_name "+
        " from ACT_RU_TASK t ,ACT_RU_EXECUTION e ,ACT_RE_PROCDEF p"+
        " where t.PROC_INST_ID_  = e.PROC_INST_ID_ "+
        " and e.PROC_DEF_ID_ = p.ID_"+
        " and t.ASSIGNEE_ = @assignee and  e.BUSINESS_KEY_ > 0 order by t.CREATE_TIME_ desc";
       // " and p.KEY_ = @processKey and t.ASSIGNEE_ = @assignee and  e.BUSINESS_KEY_ > 0 order by t.CREATE_TIME_ desc";
        Map<String,String> conditionMap = new HashMap<>();
        // conditionMap.put("processKey", processKey);
        conditionMap.put("assignee", assignee);
        List<Record> data = db.queryListBySqlMap(pageIndex,pageSize,sql,conditionMap);
        AjaxResult result = AjaxResult.success(data);
        long totalCount = db.queryCountBySqlMap(sql, conditionMap);
        result.put("recordsTotal", totalCount);
        result.put("recordsFiltered", totalCount);
        return result;
    }

    /**
     * 根据业务查询用户已办任务列表。
     *
     * @param processKey 流程定义key
     * @param assignee 用户
     * @return 任务列表
     */
    public AjaxResult queryDoneTaskList(String assignee,int pageIndex,int pageSize) {

        /*
        SELECT DISTINCT t.ID_ as task_id,t.PROC_INST_ID_ as instance_id,
            t.NAME_ as task_name,
            t.START_TIME_ as start_time,
            t.END_TIME_ as end_time,h.BUSINESS_KEY_ as data_id

            ,p.NAME_ as process_name 
            FROM ACT_HI_TASKINST t,ACT_HI_PROCINST H
                , ACT_RE_PROCDEF p
                 WHERE t.PROC_INST_ID_ = h.PROC_INST_ID_ 
				and h.PROC_DEF_ID_ = p.ID_
				and t.ASSIGNEE_ = 'lisi'
                AND t.END_TIME_ IS NOT NULL
                ORDER BY t.END_TIME_ DESC
        */
        String sql = " SELECT DISTINCT t.ID_ as task_id, t.NAME_ as task_name, t.START_TIME_ as start_time, t.END_TIME_ as end_time, "+
        " h.start_user_id_ as user_id, h.PROC_INST_ID_ as instance_id ,h.NAME_ as instance_title,h.BUSINESS_KEY_ as data_id , "+
        " p.NAME_ as process_name "+
        " FROM ACT_HI_TASKINST t,ACT_HI_PROCINST H  , ACT_RE_PROCDEF p "+
        " WHERE t.PROC_INST_ID_ = h.PROC_INST_ID_  and h.PROC_DEF_ID_ = p.ID_ "+
        " and t.ASSIGNEE_ = @assignee AND t.END_TIME_ IS NOT NULL ORDER BY t.END_TIME_ DESC";
        //" and p.KEY_ = @processKey and t.ASSIGNEE_ = @assignee AND t.END_TIME_ IS NOT NULL ORDER BY t.END_TIME_ DESC";
        Map<String,String> conditionMap = new HashMap<>();
        // conditionMap.put("processKey", processKey);
        conditionMap.put("assignee", assignee);
        List<Record> data = db.queryListBySqlMap(pageIndex,pageSize,sql,conditionMap);
        AjaxResult result = AjaxResult.success(data);
        long totalCount = db.queryCountBySqlMap(sql, conditionMap);
        result.put("recordsTotal", totalCount);
        result.put("recordsFiltered", totalCount);
        return result;
    }

    /**
     * 获取流程定义图片
     *
     * @param processDefinitionId
     * @return
     */
    public InputStream getDefinitionImage(String processDefinitionId) {
        // repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey)
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionId).latestVersion().singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getDiagramResourceName());
        return inputStream;
    }


    /**
     * 获取流程示例的执行情况图
     * @param processInstanceId
     * @return
     */
    public InputStream getResourceDiagramInputStream(String processInstanceId) {
        try {
            // 获取历史流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
                        
            // 获取流程中已经执行的节点，按照执行先后顺序排序
            List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceId().asc().list();
            
            // 构造已执行的节点ID集合
            // List<String> executedActivityIdList = new ArrayList<String>();
            // List<String> flows = new ArrayList<>();
            // for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
            //     //executedActivityIdList.add(activityInstance.getActivityId());
            //     String activityType = activityInstance.getActivityType();
            //     log.info("activityType : {}",activityType);
            //     if (activityType.equals("sequenceFlow") || activityType.equals("exclusiveGateway")) {
            //         flows.add(activityInstance.getActivityId());
            //     } else if (activityType.equals("userTask") || activityType.equals("startEvent")|| activityType.equals("endEvent")) {
            //         executedActivityIdList.add(activityInstance.getActivityId());
            //     }
            // }
            // 高亮已经执行流程节点ID集合
            List<String> highLightedActivitiIds = new ArrayList<>();
            for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
                highLightedActivitiIds.add(historicActivityInstance.getActivityId());
            }
            
            
            // 获取bpmnModel
            BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
            // 高亮流程已发生流转的线id集合
            List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstanceList);

            log.info("highLightedActivitiIds {}====",highLightedActivitiIds);
            log.info("highLightedFlowIds {}====",highLightedFlowIds);
            // List<Execution> executions = runtimeService.createExecutionQuery()
            //         .processInstanceId(processInstanceId).list();
            // //得到正在执行的Activity的Id
            // List<String> activityIds = new ArrayList<String>();
            // for (Execution exe : executions) {
            //     List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            //     activityIds.addAll(ids);
            // }
            // 使用默认配置获得流程图表生成器，并生成追踪图片字符流
            ProcessDiagramGenerator processDiagramGenerator = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
            InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitiIds, highLightedFlowIds, "宋体", "微软雅黑", "黑体", null, 2.0);
            return imageStream;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取已经流转的线
     * 
     * @param bpmnModel
     * @param historicActivityInstances
     * @return
     */
    private static List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        // 高亮流程已发生流转的线id集合
        List<String> highLightedFlowIds = new ArrayList<>();
        // 全部活动节点
        List<FlowNode> historicActivityNodes = new ArrayList<>();
        // 已完成的历史活动节点
        List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
            historicActivityNodes.add(flowNode);
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstances.add(historicActivityInstance);
            }
        }

        FlowNode currentFlowNode = null;
        FlowNode targetFlowNode = null;
        // 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
            // 获得当前活动对应的节点信息及outgoingFlows信息
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
            List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();

            /**
             * 遍历outgoingFlows并找到已已流转的 满足如下条件认为已已流转： 1.当前节点是并行网关或兼容网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最早的流转节点视为有效流转
             */
            if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
                // 遍历历史活动节点，找到匹配流程目标节点的
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
                    if (historicActivityNodes.contains(targetFlowNode)) {
                        highLightedFlowIds.add(targetFlowNode.getId());
                    }
                }
            } else {
                List<Map<String, Object>> tempMapList = new ArrayList<>();
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
                            Map<String, Object> map = new HashMap<>();
                            map.put("highLightedFlowId", sequenceFlow.getId());
                            map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
                            tempMapList.add(map);
                        }
                    }
                }

                if (!CollectionUtils.isEmpty(tempMapList)) {
                    // 遍历匹配的集合，取得开始时间最早的一个
                    long earliestStamp = 0L;
                    String highLightedFlowId = null;
                    for (Map<String, Object> map : tempMapList) {
                        long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());
                        if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) {
                            highLightedFlowId = map.get("highLightedFlowId").toString();
                            earliestStamp = highLightedFlowStartTime;
                        }
                    }

                    highLightedFlowIds.add(highLightedFlowId);
                }

            }

        }
        return highLightedFlowIds;
    }
    
}